import concurrent.futures
import textfsm
import copy
from io import StringIO
# from concurrent.futures import ThreadPoolExecutor
import re
# import glob
# import os
import time
import pathlib
from joblib import Parallel, delayed


class parse_command:
    def __init__(self,
                 filepath:pathlib.Path, # 文件路径
                 command:str, # 采集命令
                 splitstr:str, # 分割符
                 device_name_textfsm:textfsm.TextFSM,
                 parse_textfsm:textfsm.TextFSM
                 ) -> None:
        
        self.command=command
        self.filepath=filepath
        self.splitstr=splitstr
        self.device_name_textfsm=copy.deepcopy(device_name_textfsm)
        self.parse_textfsm=copy.deepcopy(parse_textfsm)
        
        # 初始数据
        self.cli_return=''
        self.device_name='UnknowDeviceName'
        self.command_return=''
        self.filename=filepath.name

        # 初始化加载数据
        self.__read_file()
        self.__get_device_name()
        self.__get_command_return()
        
    def __read_file(self):
        with self.filepath.open('r', errors='ignore',encoding='utf-8') as f:
            self.cli_return=f.read()
    
    def __get_device_name(self):
        result = self.device_name_textfsm.ParseTextToDicts(self.cli_return)
        if result:
            result = result[0]
            if result['HOSTNAME']:
                self.device_name = result['HOSTNAME']
            elif result['DEVICENAME']:
                self.device_name = result['DEVICENAME']
     
    def __get_command_return(self):
        self.command_return=self.cli_return
        if self.device_name=='UnknowDeviceName':
            return self.cli_return
        
        _COMMAND = r'\S*\s+'.join(self.command.split()) + r'\S*'
        splitstr = self.splitstr.replace('device_name', self.device_name)

        for cli_command in self.cli_return.replace(splitstr, '@@duanfu@@' + splitstr).split('@@duanfu@@'):
            if re.search(pattern=fr'{self.device_name}.*{_COMMAND}\s*[\r\n]',string=cli_command):
                self.command_return = cli_command
            elif re.search(fr'echoscripts:{_COMMAND}\s*[\r\n]',string=cli_command):
                self.command_return = cli_command
            elif re.search(fr'^\s*{_COMMAND}\s*[\r\n]',string=cli_command):
                self.command_return = cli_command
    
    
    def ParseTextToDicts(self):
        result=[]
        result_textfsm=self.parse_textfsm.ParseTextToDicts(self.command_return)
        if  result_textfsm:
            for i_info  in result_textfsm:
                result.append({'FILE_NAME':f'{self.filename}','DEVICE_NAME':self.device_name} | i_info)
        else:
             result.append({'FILE_NAME':f'{self.filename}','DEVICE_NAME':self.device_name,'RESULT':'PARSE_FAIL'})
        
        return result
        
def run_parse_command(filepath:pathlib.Path,
                      command,
                      splitstr,
                      device_name_textfsm: textfsm.TextFSM,
                      parse_textfsm: textfsm.TextFSM):
    
    parse = parse_command(filepath,
                          command,
                          splitstr,
                          device_name_textfsm,
                          parse_textfsm)
    return  parse.ParseTextToDicts()
    


class ParseText:
    '''
    # 文件路径不存在
    # 获取设备名称的textfsm模板错误
    # 解析配置文件textfsm模板错误
    '''
    
    def __init__(self,configs) -> None:
        self.configs=configs
        self.__table_name=self.configs["表格名称"]
        self.__input_path=pathlib.Path(self.configs["输入路径"])
        self.__file_type:str=self.configs["文件类型"] # *.txt;*.log
        self.__command=self.configs["采集命令"]
        self.__devicename_textfsm_str=self.configs["获取设备名称textfsm"]
        self.__textfsm_str=self.configs["解析模板"]
        self.__splitstr=self.configs["设备名称分割符"]
        
        # 校验
        self.check_config()
        

        
    
    def check_config(self):
        if self.__input_path.is_dir():
            pass
        else:
            raise Exception(f"输入文件路径不存在: {self.__input_path}")
        
        try:
            self.device_name_textfsm=textfsm.TextFSM(StringIO(self.__devicename_textfsm_str))
        except Exception as e:
            raise Exception(f"设备名解析模板错误: {e}")
        
        try:
            self.parse_textfsm=textfsm.TextFSM(StringIO(self.__textfsm_str))
        except Exception as e:
            raise Exception(f"解析模板错误: {e}")
        

    
    def run(self):
        self.runresult=[]
        with concurrent.futures.ThreadPoolExecutor() as executor:
            futures=[]
            # print(len(self.get_inputfile()))
            for filepath in self.get_inputfile():
                future=executor.submit(run_parse_command, 
                                    filepath=filepath,
                                    command=self.__command,
                                    splitstr=self.__splitstr,
                                    device_name_textfsm=self.device_name_textfsm,
                                    parse_textfsm=self.parse_textfsm)
                futures.append(future)
            for future in  concurrent.futures.as_completed(futures):
                    # print(future)
                    data = future.result()
                    # print('data = future.result()',data)
                    self.runresult+=data

    def result(self):
        return self.runresult
        

    def get_inputfile(self):
        files=[]
        for fileinfo in  self.__file_type.split(';'):
            files+=list(self.__input_path.glob(rf'**\{fileinfo}'))
        return files
            

def run(run_configs):
    ''' {
        'tablename':{'result':[],
                     'status':False,
                     'error':'',
                  }
     }'''
    result={}
    for run_config in run_configs:
        table_name=run_config["表格名称"]
        result[table_name]={'result':[],'status':False,'error':'',}
        try:
            Pars=ParseText(run_config)
            Pars.run()
            result[table_name]['result']=Pars.result()
            result[table_name]['status']=True
            # print(Pars.result())
        except Exception as e:
            result[table_name]['status']=False
            result[table_name]['error']=f"{e}"
    
    return result


def run_joblib(run_configs):
    '''
    {'华为设备核对snetconf': {'result': [111], 'status': True, 'error': ''}, }
    '''
    paese_result={}
    def run1(run_config):
        result={}
        table_name=run_config["表格名称"]
        # print(f"开始执行:{table_name}")
        result[table_name]={'result':[],'status':False,'error':'',}
        try:
            Pars=ParseText(run_config)
            Pars.run()
            result[table_name]['result']=Pars.result()
            # result[table_name]['result']=[111]
            result[table_name]['status']=True
            # print(Pars.result())
        except Exception as e:
            result[table_name]['status']=False
            result[table_name]['error']=f"{e}"
        return result
    
    results = Parallel(n_jobs=-1)(delayed(run1)(run_config) for run_config in run_configs)
    
    for result in results:
        paese_result.update(result)
    
    # print(paese_result)
    return paese_result
    


if  __name__ =='__main__':
    run_config = [
        {
        "表格名称": "华为设备核对snetconf",
        "设备类型": "华为",
        "输入路径": r"E:\python\learn\lraen_pathlib\input\城域网\BRAS\华为",
        "文件类型": "*.txt;*.log",
        "采集命令": "all",
        "解析模板": "Value snetconf (enable)\nValue netconf (netconf)\n\nStart\n  ^snetconf\\s+server\\s+${snetconf}\n  ^${netconf}",
        "获取设备名称textfsm": "Value DEVICENAME (\\S+)\nValue HOSTNAME (\\S+)\n\nStart\n  ^<${DEVICENAME}>\n  ^sysname\\s+${HOSTNAME}",
        "设备名称分割符": "<device_name>",
    },
    {
        "表格名称": "华为设备核对snetconf1",
        "设备类型": "华为",
        "输入路径": r"E:\python\learn\lraen_pathlib\input\城域网\城域网核心",
        "文件类型": "*.txt;*.log",
        "采集命令": "all",
        "解析模板": "Value snetconf (enable)\nValue netconf (netconf)\n\nStart\n  ^snetconf\\s+server\\s+${snetconf}\n  ^${netconf}",
        "获取设备名称textfsm": "Value DEVICENAME (\\S+)\nValue HOSTNAME (\\S+)\n\nStart\n  ^<${DEVICENAME}>\n  ^sysname\\s+${HOSTNAME}",
        "设备名称分割符": "<device_name>",
    },]
    
    import time
    startime=time.time()
    bb=run_joblib(run_config)
    print(bb)
    print('耗时:',time.time()-startime)
    